﻿/*
	Copyright (C) 2019 Zongming Liu <1849059316@qq.com>

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License along
	with this program; if not, write to the Free Software Foundation, Inc.,
	51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

#ifndef Widget_h__
#define Widget_h__


#include <functional>
#include <memory>
#include <vector>
#include <array>

#include "../base/Color.h"
#include "../base/Object.h"
#include "../action/IActableObject.h"
#include "../ui/Font.h"
#include "../base/Matrix.h"
#include "../base/Vector.h"
#include "../base/Rect.h"

namespace SkyEngine2d
{

	class Scale9Image;
	class EventMouse;
	class EventKey;
	class UiCore;
	class EventFocus;
	class EventCharacter;
	class EventWidgetMove;
	class EventWidgetActive;
	class Camera;
	class EventResize;
	class WidgetEvent;
	class IEvent;
	class Render;

	/*!
	 \brief 控件基类
	*/
	class SGE_API Widget
		:public IMovableObject, public Object<Widget>
	{

		UI_CLASS(Widget)

	public:

		//! 控件获取焦点的策略
		enum class FocusPolicy
		{
			TabFocus = 0x01,  /*!< 按Tab键切换. */
			ClickFocus = 0x10,/*!< 通过鼠标点击. */
			NoFocus = 0x00	/*!< 不获取焦点. */
		};

		//! 窗口的状态
		enum class WidgetState
		{
			Disabled,	/*!< 禁用. */
			Hover,		/*!< 鼠标悬停. */
			Pressed,	/*!< 鼠标按下. */
			Normal,		/*!< 正常状态. */
		};

		/*!< 窗口的状态总数. */
		static const int WidgetStateCount = 4;

		using MouseEventCallback = std::function<void(EventMouse*)>;
		using KeyEventCallback = std::function<void(EventKey*)>;

		static Widget* create(Widget* parent = nullptr);

		//! 析构
		~Widget();

		//! 将派生类的 shared_ptr 转为 widget 的 shared_ptr 
		/*!
		\return WidgetSharedPtr
		*/
		//WidgetSharedPtr convertToWidgetSharedPtr();

		//! 设置背景图片
		/*!
		\param std::unique_ptr<Scale9Image> image 背景图
		\param WidgetState state 对应的窗口状态
		\return void
		*/
		void setBackgroundImage(std::unique_ptr<Scale9Image> image, WidgetState state = WidgetState::Normal);

		//! 设置背景图片
		/*!
		\param const SpriteFrameSharedPtr & sprite_frame 精灵帧
		\param Rect cap 9宫图的中间区域  参考 \a Scale9Image
		\param WidgetState state 对应的窗口状态
		\return void
		*/
		void setBackgroundImage(const SpriteFrameSharedPtr& sprite_frame, Rect cap = Rect::Zero, WidgetState state = WidgetState::Normal);

		//! 设置背景图片
		/*!
		\param const std::wstring & file_name 图片的路径
		\param Rect cap 9宫图的中间区域  参考 \a Scale9Image
		\param WidgetState state 对应的窗口状态
		\return void
		*/
		void setBackgroundImage(const std::wstring& file_name, Rect cap = Rect::Zero, WidgetState state = WidgetState::Normal);

		//! 设置字体
		/*!
		\param const Font & font 字体
		\param WidgetState state 窗口的状态
		\return void
		*/
		void setFont(const Font& font, WidgetState state = WidgetState::Normal);

		//! 设置文字的颜色
		/*!
		\param const Color & color
		\return void
		*/
		void setTextColor(const Color4f& color, WidgetState state = WidgetState::Normal);

		//! 设置这个widget是否禁用
		/*!
		\param bool enabled
		\return void
		\sa isEnabled()const
		*/
		void setEnabled(bool enabled);

		//! 这个widget是否被禁用
		/*!
		\return bool
		\sa setEnabled(bool enabled)
		*/
		bool isEnabled()const;

		//! 获取某个位置的子控件的弱引用 ，如果不存在 则返回为null
		/*!
		\param const Vector2 & pos 需要获取的位置
		\param const bool allowDisable 是否获取被禁用的子控件
		\return WidgetSharedPtr
		*/
		Widget* getChildAtPos(const Vector2& pos, const bool allowDisable);

		//! 移动控件到某个位置（相对于父控件）
		/*!
		\param float x  横坐标
		\param float y  纵坐标
		\return void
		单位为(px)像素，目标位置以这个控件的左下角为锚点进行计算
		/sa const Vector2 & getPos()const
		*/
		void move(float x, float y);

		//! 移动控件到某个位置（相对于父控件）
		/*!
		\param const Vector2 & pos 位置
		\return void
		单位为(px)像素，目标位置以这个控件的左下角为锚点进行计算
		/sa const Vector2 & getPos()const
		*/
		void move(const Vector2& pos);

		//! 获取控件左下角的位置（相对于父控件）
		/*!
		\return  Vector2
		\sa void move(float x, float y)
		*/
		virtual Vector2 getPosition() const override;

		//! 设置widget 的位置 等价于 move(const Vector2 & pos)
		/*!
		\param const Vector2 & pos
		\return void
		*/
		virtual void setPosition(const Vector2& pos) override;

		//! 获取控件左下角的位置 （绝对坐标，即相对与当前的根窗口）
		/*!
		\return const Vector2 &
		\sa const Vector2 & getPos()const;
		*/
		Vector2  getAbsPos()const;

		//! 设置控件的矩形表示
		/*!
		\param const Rect & rect 矩形
		\return void
		每个控件都是一个矩形，这个矩形区域用来作为展示控件背景、鼠标进出检测等的依据。
		这个矩形是由左下角坐标和矩形的宽高两组信息组成的。坐标是相对于父控件的。
		\sa const Rect & getGeometry()const
		*/
		void setGeometry(const Rect& rect);

		//! 获取控件的矩形表示
		/*!
		\return const Rect &
		每个控件都是一个矩形，这个矩形区域用来作为展示控件背景、鼠标进出检测等的依据。
		这个矩形是由左下角坐标和矩形的宽高两组信息组成的。坐标是相对于父控件的。
		\sa void setGeometry(const Rect & rect)
		*/
		const Rect& getGeometry()const;

		//! 调整控件的尺寸
		/*!
		\param const Size & size 尺寸
		\return void
		*/
		void resize(const Size& size);

		//! 获取控件的尺寸
		/*!
		\return const D2D1::Size &
		*/
		const Size& getSize()const;

		//! 获取控件是否在活跃状态
		/*!
		\return bool
		*/
		bool isActive()const;

		//! 获取控件是否拥有焦点
		/*!
		\return bool
		*/
		bool hasFocus()const;

		//! 控件是否被显示
		/*!
		\return bool
		*/
		bool isVisible()const;

		//! 隐藏窗口
		/*!
		\return void
		*/
		void hide();

		//! 显示
		/*!
		\return void
		*/
		void show();

		//! 获取控件的相机
		/*!
		\return Camera	*
		\sa void setCamera(Camera * camera)
		*/
		Camera* getCamera()const;

		//! 设置控件的相机
		/*!
		\param Camera * camera
		\return void
		\sa Camera	* getCamera()const
		*/
		void setCamera(Camera* camera);

		void setFocuse(bool focuse = true);

		FocusPolicy getFocusPolicy()const;

		void setFocusPolicy(FocusPolicy policy);

		bool isDistributeCapturedInputs()const;

		Widget* getActiveChiled()const;

		UiCore* getUiCore()const;

		void setMousePressEventListener(const MouseEventCallback& call);

		void setMouseReleaseEventListener(const MouseEventCallback& call);

		void setMouseMoveEventListener(const MouseEventCallback& call);

		void setMouseBoubleClickedEventListener(const MouseEventCallback& call);

		virtual void visit(IRender* render, bool parent_change_transfrom_mat);

	protected:

		struct StyleInfor
		{
			std::unique_ptr<Scale9Image> backgroundImage; /*!< 背景图. */
			Color4f						 textColor;      /*!< 文本的颜色. */
			Font						 textFont;		/*!< 文本的字体. */
		};

		friend class UiCore;

		//! 构造
		/*!
		\param Widget * parent 父窗口
		*/
		Widget(Widget* parent = nullptr);

		//! 初始化窗口
		/*!
		\return bool
		\note 子类在初始化阶段 务必先调用 父类的初始化
		*/
		virtual bool init();

		//! 键盘中的键按下事件
		/*!
		\param EventKey * event 按键事件
		\return void
		*/
		virtual void onKeyPressEvent(EventKey* event);

		//! 键盘中的键弹起事件
		/*!
		\param EventKey * event 键盘事件
		\return void
		*/
		virtual void onKeyReleaseEvent(EventKey* event);

		//! 鼠标键按下事件
		/*!
		\param EventMouse * event 键盘事件
		\return void
		*/
		virtual void onMousePressEvent(EventMouse* event);

		//! 鼠标键弹起事件
		/*!
		\param EventMouse * event 鼠标事件
		\return void
		*/
		virtual void onMouseReleaseEvent(EventMouse* event);

		//! 鼠标移动事件
		/*!
		\param EventMouse * event 鼠标事件
		\return void
		*/
		virtual void onMouseMoveEvent(EventMouse* event);

		//! 鼠标双击事件
		/*!
		\param EventMouse * event 鼠标事件
		\return void
		*/
		virtual void onMouseDoubleClickedEvent(EventMouse* event);

		//! 鼠标进入窗口
		/*!
		\param EventMouse * event 鼠标事件
		\return void
		*/
		virtual void onMouseLeavesEvent(WidgetEvent* event);

		//! 鼠标离开窗口
		/*!
		\param EventMouse * event 鼠标事件
		\return void
		*/
		virtual void onMouseEnterEvent(WidgetEvent* event);

		//! 获得焦点事件
		/*!
		\param EventFocus * event 焦点事件
		\return void
		*/
		virtual void onFocusInEvent(EventFocus* event);

		//! 失去焦点事件
		/*!
		\param EventFocus * event 焦点事件
		\return void
		*/
		virtual void onFocusOutEvent(EventFocus* event);

		//! 字符消息事件
		/*!
		\param EventCharacter * e 字符事件
		\return void
		当键盘输入的按键可打印或者输入法有输入时，将会触发这个事件
		*/
		virtual void onCharacterEvent(EventCharacter* e);

		//! 窗口移动事件
		/*!
		\param EventWidgetMove * e 窗口移动事件
		\return void
		*/
		virtual void onMoveEvent(EventWidgetMove* e);

		//! 窗口进入活动状态的事件
		/*!
		\param EventWidgetActive * e 事件
		\return void
		*/
		virtual void onActiveEvent(EventWidgetActive* e);

		//! 窗口失去活动状态的事件
		/*!
		\param EventWidgetActive * e 事件
		\return void
		*/
		virtual void onDeactiveEvent(EventWidgetActive* e);

		//! 窗口的尺寸改变的事件
		/*!
		\param EventResize * e 事件
		\return void
		*/
		virtual void onResizeEvent(EventResize* e);

		virtual bool onEvent(IEvent* e);

		//! 获取活动的兄弟窗口
		/*!
		\return Widget *
		*/
		Widget* getActiveSibling()const;

		//! 获取父窗口的矩阵
		/*!
		\return const Matrix4X4&
		*/
		const Matrix4X4& getParentMat();

		//! 移动窗口到前边
		/*!
		\return void
		*/
		void moveToFront();

		//! 捕获焦点
		/*!
		\return void
		*/
		void captureFocus();

		//! 释放焦点
		/*!
		\return void
		*/
		void releaseFocus();

		//! 获取当前窗口状态的风格
		/*!
		\return const Widget::StyleInfor *
		*/
		const StyleInfor* getStyleInfor() const;

		//! 获取指定窗口状态的风格
		/*!
		\param WidgetState state  窗口状态
		\return const Widget::StyleInfor* 风格
		*/
		const StyleInfor* getStyleInforByState(WidgetState state)const;

		void  setCurrentWidgetState(WidgetState state);

		WidgetState getCurrentWidgetState()const;

	private:
		//! 获取这个控件的默认宽度
		/*!
		\return int
		开发新的控件一般需要重写这个函数，以便提供适应该控件的默认宽度
		\sa int getDefultHeight()const
		*/
		virtual int getDefultWidth()const;

		//! 获取这个控件的默认高度
		/*!
		\return int
		开发新的控件一般需要重写这个函数，以便提供适应该控件的默认高度
		\sa int getDefultWidth()const
		*/
		virtual int getDefultHeight()const;

		//! 绘制窗口
		/*!
		\param Render * render
		\param const Matrix4X4 & transform
		\return void
		*/
		virtual void draw(IRender* render, const Matrix4X4& transform);

		virtual void onWidgetStateChange();
		virtual void onWidgetStyleChange();
		//! 更新矩阵信息
		/*!
		\param bool parent_change 父节点矩阵信息是否改变
		\return bool	当前矩阵是否发生改变
		*/
		bool updateMat(bool parent_change);
	private:
		const std::unique_ptr<StyleInfor>& getStyleInfoPtr(WidgetState state);

	protected:

		Rect m_geometry;					/*!< 控件的矩形表示. 相对与父控件 */

	private:

		bool m_isEnabled;					/*!< 是否启用这个控件. */
		bool m_isMouseTracking;				/*!< 是否启动鼠标跟踪. */ //TODO 可能需要调整 
		bool m_isDistributeCapturedInputs;	/*!< 是否分发捕获的输入给子控件. */
		bool m_hasFocus;					/*!< 是否获得焦点. */
		bool m_isVisible;					/*!< 是否可见. */
		bool m_isChangeMat;					/*!< 变换矩阵是否发生改变. */
		bool m_isActive;					/*!< 是否在活动状态. */

		FocusPolicy	m_focusPolicy;			/*!< 获取焦点的策略 */

		MouseEventCallback				m_mousePressEventCall;
		MouseEventCallback				m_mouseReleaseEventCall;
		MouseEventCallback				m_mouseMoveEventCall;
		MouseEventCallback				m_mouseDoubleClickedEventCall;

		Matrix4X4						m_transformMat;

		UiCore* m_uiCore;			/*!< Ui组件的核心. */
		Camera* m_camera;			/*!< 相机. */

		WidgetState	m_widgetState;  /*!< 窗口状态. */
		WidgetState m_lastWidgetState; /*!< 窗口的上一个状态. */

		std::array<std::unique_ptr<StyleInfor>, Widget::WidgetStateCount> m_styleInfoArr; /*!< 风格数组. */

	};
}

#endif // Widget_h__
